

const own = {};

var ua = navigator.userAgent.toLowerCase(),
    match = ua.match(/(opera|firefox|chrome|version)[\s\/:]([\w\d\.]+)?.*?(safari|version[\s\/:]([\w\d\.]+)|$)/) || [null, 'unknown', '0'];

own['browser'] = (match[1] === 'version') ? match[3] : match[1];                                 //浏览器类型opera|firefox|chrome|safari
own['version'] = parseFloat((match[1] === 'opera' && match[4]) ? match[4] : match[2]);

//只有ie有documentMode
if (document.documentMode) {
    own['browser'] = 'ie';
    own['version'] = document.documentMode; //ie浏览器版本
}

//新版本的opera，会同时有chrome和safari的信息，所以用opr
const opMatch = ua.match(/(opr)[\s\/:]([\w\d\.]+)?/);
if (opMatch) {
    own['browser'] = 'opera';
    own['version'] = opMatch[2];
}

own[own['browser']] = parseInt(own['version'], 10);                                              //浏览器类型名称作为属性，值为版本整数值，可以当boolean值用
own[own['browser'] + parseInt(own['version'], 10)] = true;                                       //浏览器类型加版本号作为属性值为true

own['webkit'] = (ua.indexOf('webkit') != -1);

export const isDef = (value) => {
    //return typeof value != 'undefined'; 慢
    return value !== undefined;
};
own.isDef = isDef;

export const isNull = (value) => {
    return value !== null;
};
own.isNull = isNull;

export const isArr = (value) => {
    return Object.prototype.toString.call(value) === '[object Array]';
};
own.isArr = isArr;

export const isDate = (value) => {
    return Object.prototype.toString.call(value) === '[object Date]';
};
own.isDate = isDate;

export const isObj = (own['ie'] <= 8) ? (value) => {
    return !!value && Object.prototype.toString.call(value) === '[object Object]' && !!value.hasOwnProperty && !value.callee;
} : (value) => {
    return Object.prototype.toString.call(value) === '[object Object]';
};
own.isObj = isObj;

export const isFun = (value) => {
    //return Object.prototype.toString.call(value) === '[object Function]'; 慢
    return typeof value === 'function';
};
own.isFun = isFun;

export const isNum = (value) => {
    //return Object.prototype.toString.call(value) === '[object Number]' && isFinite(value); 慢
    //除去Infinity和NaN
    return (typeof value === 'number') && isFinite(value);
};
own.isNum = isNum;

export const isStr = (value) => {
    return typeof value === 'string';
};
own.isStr = isStr;

export const isBool = (value) => {
    //return Object.prototype.toString.call(value) === '[object Boolean]'; 慢
    return typeof value === 'boolean';
};
own.isBool = isBool;

export const isDom = (value) => {
    return isNode(value) || isWin(value);
};
own.isDom = isDom;

export const isNode = (value) => {
    return value ? !!value.nodeType : false;
};
own.isNode = isNode;

export const isTag = (value) => {
    return value ? !!value.tagName : false;
};
own.isTag = isTag;

export const isBody = (value) => {
    return (/^(?:body|html)$/i).test(value.tagName);
};
own.isBody = isBody;

export const isDoc = (value) => {
    return !!value && value.nodeType === 9;
};
own.isDoc = isDoc;

export const isWin = (value) => {
    var str = value ? value.toString() : '';
    return str == '[object Window]' || str == '[object DOMWindow]';
};
own.isWin = isWin;

// 比旧版增加了一个方法
export const isEmpty = (value) => {
    return !value && !isNum(value);
};
own.isWin = isWin;

// 比旧版新增了regexp类型
export const typeOf = (item) => {
    if (item === null) {
        return 'null';
    }
    if (isArr(item)) {
        return 'array';
    }
    if (isDate(item)) {
        return 'date'
    }
    if (item && item instanceof RegExp) {
        return 'regexp';
    }
    if (item.nodeName) {
        if (item.nodeType === 1) {
            return 'element';
        }
        if (item.nodeType === 3) {
            return (/\S/).test(item.nodeValue) ? 'textnode' : 'whitespace';
        }
    }
    else if (typeof item.length === 'number') {
        if (item.callee) {
            return 'arguments';
        }
    }

    return typeof item;
};
own.typeOf = typeOf;

/* 相对于旧版有化简，减少参数和返回值设定 */
export const interceptor = (fn, filter, scope) => {
    if (!isFun(filter)) {
        return fn;
    }
    else {
        return function () {
            return (filter.apply(scope, arguments) !== false) ? fn.apply(scope, arguments) : null;
        };
    }
};
own.interceptor = interceptor;

/* 相对旧版有修改，作用域字段名改变，默认作用域改变 */
export const delegate = (fn, scope, args, appendArgs) => {

    if (!isFun(fn)) {
        return fn;
    }
    return function () {
        let callArgs;

        if (appendArgs === true) {
            callArgs = Array.prototype.slice.call(arguments, 0);
            callArgs = callArgs.concat(args);
        }
        else if (isNum(appendArgs)) {
            callArgs = Array.prototype.slice.call(arguments, 0);
            Array.prototype.splice.call(callArgs, appendArgs, 0, ...args);
        }
        else {
            callArgs = args || arguments;
        }
        return fn.apply(scope, callArgs);
    };
};
own.delegate = delegate;

/* 相对旧版有修改，作用域字段名改变，默认作用域改变 */
export const defer = (fn, millis, scope, args, appendArgs) => {
    const newFn = delegate(fn, scope, args, appendArgs);
    if (millis > 0) {
        return setTimeout(newFn, millis);
    } else {
        newFn();
    }

    return 0;
};
own.defer = defer;

/* 相对旧版有修改，作用域字段名改变，默认作用域改变 */
export const sequence = (fn, nextFn, scope) => {

    if (!isFun(nextFn)) {
        return fn;
    }
    else {
        return function () {
            const retval = fn.apply(scope, arguments);
            nextFn.apply(scope, arguments);
            return retval;
        };
    }
};
own.sequence = sequence;

export const limit = (num, min, max) => {
    return Math.min(max, Math.max(min, num));
};
own.limit = limit;

/* 相对旧版有修改，加paserFloat转换 */
export const round = (num, precision) => {
    precision = parseFloat(Math.pow(10, precision || 0).toFixed(precision < 0 ? -precision : 0));
    return Math.round(num * precision) / precision;
};
own.round = round;

/* 相对旧版参数名bind改成scope */
export const times = (num, fn, scope) => {
    for (var i = 0; i < num; i++) {
        fn.call(scope, i, num);
    }
};
own.times = times;

export const test = (string, regex, params) => {
    let reg = (typeOf(regex) === 'regexp') ? regex : new RegExp('' + regex, params);
    return reg.test(string);
};
own.test = test;

export const trim = (string) => {
    return string ? string.replace(/^\s+|\s+$/g, '') : '';
};

export const camelCase = (string) => {
    return string ? string.replace(/-\D/g, function (match) {
        return match.charAt(1).toUpperCase();
    }) : '';
};
own.camelCase = camelCase;

export const hyphenate = (string) => {
    return string ? string.replace(/[A-Z]/g, function (match) {
        return ('-' + match.charAt(0).toLowerCase());
    }) : '';
};
own.hyphenate = hyphenate;

export const capitalize = (string) => {
    return string ? string.replace(/\b[a-z]/g, function (match) {
        return match.toUpperCase();
    }) : '';
};
own.capitalize = capitalize;

export const escapeRegExp = (string) => {
    return string ? string.replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1') : '';
};
own.escapeRegExp = escapeRegExp;

export const substitute = (string, object, regexp) => {
    return string ? string.replace(regexp || (/\\?\{([^{}]+)\}/g), function (match, name) {
        if (match.charAt(0) == '\\') {
            return match.slice(1);
        }
        return (object[name] !== null && object[name] !== undefined) ? object[name] : '';
    }) : '';
};
own.substitute = substitute;

/*
* Date对象转字符串
* @param date 要转换的Date对象
* @param pattern 字符串格式模板，其中字符"y"、"M"、"d"、"h"、"m"、"s"、"w"、"q"、"S"会被替代成日期对应的数值，长度由字符数决定
* @return 满足pattern格式的字符串
* 例：
* pattern 为 "yyyy-MM-dd"，则返回如 "2015-08-18"
* pattern 为 "yyyy-MM-dd hh:mm:ss"，则返回如 "2015-08-18 09:41:13"
* pattern 为 "yyyy-MM-dd hh:mm:ss"，则返回如 "2015-08-18 09:41:13"
* pattern 为 "yyyy-MM-dd hh:mm:ss +(S)"，则返回如 "2015-08-18 09:43:44 +(902)"
* pattern 为 "yy年第q季度"，则返回如 "15年第3季度"
* pattern 为 "今天是星期w"，则返回如 "今天是星期二"
* */
// 跟旧版不同是字符串方法和正则方法用了标准方法
export const dateToStr = (date, pattern) => {
    if (isStr(date)) {
        return date;
    }

    pattern = pattern || 'yyyy-MM-dd';

    const d = date;

    var o = {
        "M+": d.getMonth() + 1, //month 从 Date 对象返回月份 (0 ~ 11)，加以改成1~12月份
        "d+": d.getDate(),      //day 从 Date 对象返回一个月中的某一天 (1 ~ 31)。
        "h+": d.getHours(),     //hour 返回 Date 对象的小时 (0 ~ 23)。
        "m+": d.getMinutes(),   //minute  返回 Date 对象的分钟 (0 ~ 59)。
        "s+": d.getSeconds(),   //second 返回 Date 对象的秒数 (0 ~ 59)。
        "w+": "\u65e5\u4e00\u4e8c\u4e09\u56db\u4e94\u516d".charAt(d.getDay()),   //“日一二三四五六”中的某一个
        "q+": Math.floor((d.getMonth() + 3) / 3),  //季度
        "S": d.getMilliseconds() //Date 对象的毫秒(0 ~ 999)。
    };

    //将"yyyy-MM-dd"中的"y"替换成具体年份，并根据"y"的数量保留对应的位数
    const retY = /(y+)/.exec(pattern);
    if (retY) {
        pattern = pattern.replace(retY[1], (d.getFullYear() + "").substring(4 - retY[1].length));
    }

    //替换剩下的模板，如果有的模板长度大于1，如"yyyy-MM-dd"中的"MM"、"dd"，则定为两位，实际字符串长度不足两位的，前面用0补足
    for (const k in o) {
        const ret = new RegExp('(' + k + ')').exec(pattern)
        if (ret) {
            pattern = pattern.replace(ret[1], (ret[1].length === 1) ? (o[k] + '') : ('00' + o[k]).substring(('' + o[k]).length))
        }
    }

    return pattern;
};
own.dateToStr = dateToStr;

/*
* 字符串转换成Date对象
* @param str 要转换的字符串
* @param delimiter 字符串年月日分隔符，默认为 "-"
* @param pattern 字符串年月日模板（年月日的索引对应字符串按delimiter分割后的数组索引）
* @return 日期对象
* 例：
* own.strToDate('2015-03-28') 返回Sat Mar 28 2015 00:00:00 GMT+0800 (中国标准时间) 的Date对象
* own.strToDate('2015/03/28', '/') 返回Sat Mar 28 2015 00:00:00 GMT+0800 (中国标准时间) 的Date对象
* own.strToDate('03/28/2015', '/', 'mdy') 返回 Sat Mar 28 2015 00:00:00 GMT+0800 (中国标准时间) 的Date对象
* own.strToDate('2023年5月10日', /年|月|日/) 返回 Wed May 10 2023 00:00:00 GMT+0800 (中国标准时间) 的Date对象
* own.strToDate('5月10日2023年', /年|月|日/, , 'mdy') 返回 Wed May 10 2023 00:00:00 GMT+0800 (中国标准时间) 的Date对象
* */
// 与旧版相比重写了方法
export const strToDate = (str, delimiter, pattern) => {
    if (isDate(str)) {
        return str;
    }

    const newDate = new Date(str);
    if (newDate.toString() !== 'Invalid Date') {
        return newDate;
    }

    delimiter = delimiter || "-";
    pattern = pattern || "ymd";

    const s = str;

    let a = s.split(delimiter);//当前字符串值的年月日数组
    let y = parseInt(a[pattern.indexOf("y")], 10);//年的整数值

    //如果年的值小，则为20xx年
    if (y.toString().length <= 2) {
        y += 2000;
    }

    //如果年不是数字，则取当前年
    if (isNaN(y)) {
        y = new Date().getFullYear();
    }

    let m = parseInt(a[pattern.indexOf("m")], 10) - 1;//月为字符串月的整数值减一，变成下班从0开始

    let d = parseInt(a[pattern.indexOf("d")], 10);//字符串日的整数值

    //如果日不是数字，则是1日
    if (isNaN(d)) {
        d = 1;
    }

    return new Date(y, m, d);
};
own.strToDate = strToDate;

export const addYear = (date, n, month, day) => {
    return new Date(date.getFullYear() + (n || 0), month || date.getMonth(), day || date.getDate());
};
own.addYear = addYear;

// 返回date所在月份的后面第n个月，日为day或与当前日相同，如果当前日超过后面第n个月的最大日数，则返回后面第n + 1个月的稍前日
export const addMonth = (date, n, day) => {
    return new Date(date.getFullYear(), date.getMonth() + (n || 0), day || date.getDate());
};
own.addMonth = addMonth;

export const addDay = (date, n) => {
    return new Date(date.getTime() + (n || 0) * 86400000); // 86400000 == 24 * 60 * 60 * 1000
};
own.addDay = addDay;

export const yearFirstDay = (date) => {
    return new Date(date.getFullYear(), 0, 1);
};
own.yearFirstDay = yearFirstDay;

export const yearLastDay = (date) => {
    return new Date(date.getFullYear(), 12, 0);
};
own.yearLastDay = yearLastDay;

export const monthFirstDay = (date) => {
    return new Date(date.getFullYear(), date.getMonth(), 1);
};
own.monthFirstDay = monthFirstDay;

export const monthLastDay = (date) => {
    return new Date(date.getFullYear(), date.getMonth() + 1, 0);
};
own.monthLastDay = monthLastDay;

export const indexOf = (array, item, from) => {
    if (isFun(array.indexOf)) {
        return array.indexOf(item, from);
    } else {
        const length = array.length;
        const f = from || 0;
        for (let i = (f < 0) ? Math.max(0, length + f) : f || 0; i < length; i++) {
            if (array[i] === item) return i;
        }
        return -1;
    }
};
own.indexOf = indexOf;

export const append = (array, newArray) => {
    array.push.apply(array, newArray);
    return array;
};
own.append = append;

export const getLast = (array) => {
    return (array.length) ? array[array.length - 1] : null;
};
own.getLast = getLast;

// 与旧版相比合并了combine方法
// export const include = (array: any[], item) => {
//     if (!contains(array, item)) {
//         array.push(item);
//     }
//     return array;
// };
// own.include = include;

// export const combine = (array: any[], newArray: any[]) => {
//     for (var i = 0, l = newArray.length; i < l; i++) {
//         array.include(newArray[i]);
//     }
//     return array;
// };
// own.combine = combine;

// 与旧版相比修改了参数规则
export const pick = (array, ignore ) => {
    if (isFun(ignore)) {
        for (var i = 0, l = array.length; i < l; i++) {
            if (ignore(array[i])) {
                return array[i];
            }
        }
    } else {
        for (var i = 0, l = array.length; i < l; i++) {
            if (!!array[i]) {
                return array[i];
            }
        }
    }

    return null;
};
own.pick = pick;

// 与旧版相比只支持对象混人
export const merge = (source, ...args) => {

    for (var i = 0, l = args.length; i < l; i++) {
        var object = args[i];
        for (var key in object) {
            const objectProp = object[key];
            if (isObj(objectProp)) {
                if (isObj(source[key])) {
                    source[key] = merge(source[key], objectProp);
                }
                else {
                    source[key] = merge({}, objectProp);
                }
            }
            else {
                source[key] = clone(objectProp);
            }
        }
    }

    return source;
};
own.merge = merge;

// 与旧版相比只支持对象混人
export const mergeIf = (source, ...args) => {

    for (var i = 0, l = args.length; i < l; i++) {
        var object = args[i];
        for (var key in object) {
            const objectProp = object[key];
            if (isObj(objectProp)) {
                if (isObj(source[key])) {
                    source[key] = merge(source[key], objectProp);
                }
                else {
                    source[key] = merge({}, objectProp);
                }
            }
            else {
                if (!isDef(source[key])) {
                    source[key] = clone(objectProp);
                }
            }
        }
    }

    return source;
};
own.mergeIf = mergeIf;

// 旧版叫aquire
export const assign = (source, ...args) => {

    for (var i = 0, l = args.length; i < l; i++) {
        var object = args[i];
        for (var key in object) {
            source[key] = object[key];
        }
    }

    return source;
};
own.assign = assign;

// 旧版叫aquireIf
export const assignIf = (source, ...args) => {

    for (var i = 0, l = args.length; i < l; i++) {
        var object = args[i];
        for (var key in object) {
            if (!isDef(source[key])) {
                source[key] = object[key];
            }
        }
    }

    return source;
};
own.assignIf = assignIf;


export const subset = (source, ...args) => {
    var results = {};

    for (var i = 0, l = args.length; i < l; i++) {
        var k = args[i];
        if (k in source) {
            results[k] = source[k];
        }
    }
    return results;
};
own.subset = subset;

export const keys = (object) => {
    if (isFun(Object.keys)) {
        return Object.keys(object);
    } else {
        var keys = [];
        for (var key in object) {
            if (object.hasOwnProperty && object.hasOwnProperty(key)) {
                keys.push(key);
            }
        }
        return keys;
    }
};
own.keys = keys;

export const values = (object) => {
    if (isFun(Object.values)) {
        return Object.values(object);
    } else {
        var values = [];
        for (var key in object) {
            if (object.hasOwnProperty && object.hasOwnProperty(key)) {
                values.push(object[key]);
            }
        }
        return values;
    }
};
own.values = values;

export const getLength = (object) => {
    return keys(object).length;
};
own.getLength = getLength;

export const keyOf = (object, value) => {
    for (var key in object) {
        if (object.hasOwnProperty && object.hasOwnProperty(key) && object[key] === value) {
            return key;
        }
    }
    return null;
};
own.keyOf = keyOf;

export const clone = (object) => {
    let clone;
    if (isArr(object)) {
        clone = [];
        for (var i = 0, len = object.length; i < len; i++) {
            clone.push(clone(object[i]));
        }
    }
    else if (isObj(object)) {
        clone = {};
        for (var key in object) {
            clone[key] = clone(object[key]);
        }
    }
    else {
        clone = object;
    }
    return clone;
};
own.clone = clone;

// 根旧版比去掉倒序参数，加入原生支持分支
export const forEach = (object, fn, bind) => {

    if (isObj(object)) {
        for (var key in object) {
            if (object.hasOwnProperty && object.hasOwnProperty(key)) {
                fn.call(bind, object[key], key, object);
            }
        }
    }
    else if (isArr(object) || (object && object.length)) {
        if (isFun(object.forEach)) {
            object.forEach(fn, bind);
        }
        else {
            for (var i = 0, l = object.length; i < l; i++) {
                fn.call(bind, object[i], i, object);
            }
        }
    }
};
own.forEach = forEach;

// 根旧版比去掉倒序参数，加入原生支持分支，同时改变分支先后
export const every = (object, fn, bind) => {
    if(isObj(object)) {
        for (var key in object) {
            if (object.hasOwnProperty && object.hasOwnProperty(key)) {
                if (!fn.call(bind, object[key], key, object)) {
                    return false;
                }
            }
        }
    } else if(isArr(object) || (object && object.length)) {
        if (isFun(object.every)) {
            return object.every(fn, bind);
        }
        else {
            for (var i = 0, l = object.length; i < l; i++) {
                if (!fn.call(bind, object[i], i, object)) {
                    return false;
                }
            }
        }
    }

    return true;
};
own.every = every;

// 根旧版比去掉倒序参数，加入原生支持分支，同时改变分支先后
export const some = (object, fn, bind) => {
    if(isObj(object)) {
        for (var key in object) {
            if (object.hasOwnProperty && object.hasOwnProperty(key)) {
                if (fn.call(bind, object[key], key, object)) {
                    return true;
                }
            }
        }
    } else if(isArr(object) || (object && object.length)) { 
        if (isFun(object.some)) {
            return object.some(fn, bind);
        }
        else {
            for (var i = 0, l = object.length; i < l; i++) {
                if (fn.call(bind, object[i], i, object)) {
                    return true;
                }
            }
        }
    }
    return false;
};
own.some = some;

// 根旧版比去掉倒序参数，加入原生支持分支，同时改变分支先后，返回值变成空数组
export const filter = (object, fn, bind) => {
    if(isObj(object)) {
        const results = {};

        for (var key in object) {
            if (object.hasOwnProperty && object.hasOwnProperty(key)) {
                if (fn.call(bind, object[key], key, object)) {
                    results[key] = object[key];
                }
            }
        }

        return results;
    } else if(isArr(object) || (object && object.length)) { 
        if (isFun(object.filter)) {
            return object.filter(fn, bind);
        }
        else {
            const results = [];
            for (var i = 0, l = object.length; i < l; i++) {
                if (fn.call(bind || this, object[i], i, object)) {
                    results.push(object[i]);
                }
            }
            return results;
        }
    }
};
own.filter = filter;

export const map = (object, fn, bind) => {
    if(isObj(object)) {
        const results = {};
        for (var key in object) {
            if (object.hasOwnProperty && object.hasOwnProperty(key)) {
                results[key] = fn.call(bind, object[key], key, object);
            }
        }
        return results;
    } else if(isArr(object) || (object && object.length)) { 
        if (isFun(object.map)) {
            return object.map(fn, bind);
        }
        else {
            const results = [];
            for (var i = 0, len = object.length; i < len; i++) {
                results.push(fn.call(bind, object[i], i, object));
            }
            return results;
        }
    }
};
own.map = map;

// 比旧版修改了空条件
export const clean = (object) => {
    if (isStr(object)) {
        return trim(object.replace(/\s+/g, ' '));
    }
    else {
        return filter(object, (item) => {
            return !isEmpty(item);
        });
    }
};
own.clean = clean;

// 旧版是contains
export const includes = (object, item, from) => {
    if (isStr(object)) {
        if (isFun(object.includes)) {
            return object.includes(item, from);
        } else {
            return object.indexOf(item) !== -1;
        }
    }
    else if (isArr(object) || (object && object.length)) {
        if (isFun(object.includes)) {
            return object.includes(item, from);
        } else {
            return indexOf(object, item, from) !== -1;
        }
    }
    else {
        return keyOf(object, item) !== null;
    }
};
own.includes = includes;

export const erase = (object, item) => {
    if (isArr(object)) {
        for (var i = object.length; i--;) {
            if (object[i] === item) {
                object.splice(i, 1);
            }
        }
    }
    else {
        for (var key in object) {
            if (object.hasOwnProperty && object.hasOwnProperty(key) && key === item) {
                delete object[key];
            }
        }
    }
    return object;
};
own.erase = erase;

export const empty = (object) => {
    if (isArr(object)) {
        object.length = 0;
    }
    else {
        for (var key in object) {
            if (object.hasOwnProperty && object.hasOwnProperty(key)) {
                delete object[key];
            }
        }
    }
    return object;
};
own.empty = empty;

export const getDocBody = () => {
    return document.compatMode == 'CSS1Compat' ? document.documentElement : document.body;
};
own.getDocBody = getDocBody;


export const execScript = (text) => {
    if (!text) {
        return text;
    }

    //IE支持window.execScript
    if (window.execScript) {
        window.execScript(text);
    }
    else {
        var script = document.createElement('script');
        script.setAttribute('type', 'text/javascript');
        script.text = text;
        document.head.appendChild(script);
        // document.head.removeChild(script);
    }
    return text;
};
own.execScript = execScript;

export const stripScripts = (text, exec) => {
    var scripts = '';
    var text = text.replace(/<script[^>]*>([\s\S]*?)<\/script>/gi, function (all, code) {
        scripts += code + '\n';
        return '';
    });

    if (exec === true) {
        execScript(scripts);
    }
    else if (isFun(exec)) {
        exec(scripts, text);
    }

    return text;
};
own.stripScripts = stripScripts;

export const loadScripts = (text) => {
    var text = text.replace(/<script([^>]*?)>[^<>]*<\/script>/gi,  (all, code) => {
        var src = code.match(/\ssrc=["']([\s\S]*)['"]/i)[1];
        importScript(src);
        return all;
    });

    return text;
};
own.loadScripts = loadScripts;

export const importScript = (src) => {
    var script = document.createElement('script');
    script.setAttribute('type', 'text/javascript');
    script.setAttribute('src', src);
    document.head.appendChild(script);
    //document.head.removeChild(script);
};
own.importScript = importScript;

export const rgbToHex = (string) => {
    var rgb = string.match(/\d{1,3}/g);
    if (rgb) {
        if (rgb.length < 3) {
            return null;
        }
        if (rgb.length === 4 && rgb[3] === '0') {
            return 'transparent';
        }

        var hex = [];
        for (var i = 0; i < 3; i++) {
            var bit = (parseInt(rgb[i]) - 0).toString(16);
            hex.push((bit.length == 1) ? '0' + bit : bit);
        }
        return '#' + hex.join('');
    }
    else {
        return null;
    }
};
own.rgbToHex = rgbToHex;

export const hexToRgb = (string, opacity) => {
    var hex = string.match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/);
    if (hex) {
        var rgbs = hex.slice(1),
            len = rgbs.length,
            rgb = [];

        if (len !== 3) {
            return null;
        }

        for (var i = 0; i < len; i++) {
            var value = rgbs[i];
            if (value.length == 1) {
                value += value;
            }
            rgb.push(parseInt(value, 16));
        }

        if (own.ie <= 8) {
            return 'rgb(' + rgb + ')';
        }
        else {
            if (typeof opacity !== 'undefined') {
                rgb.push(opacity)
            }
            else {
                rgb.push(1)
            }
            return 'rgba(' + rgb + ')';
        }

    }
    else {
        return null;
    }
};
own.hexToRgb = hexToRgb;

export const dateToJson = (window.JSON && window.JSON.stringify) ? function(date) {
    return window.JSON.stringify(date);
} : (function() {
    var f = function (n) {
        return n < 10 ? '0' + n : n;
    };

    return function (date) {
        // Invalid Date
        return isFinite(date.valueOf()) ?
            date.getUTCFullYear() + '-' +
            f(date.getUTCMonth() + 1) + '-' +
            f(date.getUTCDate()) + 'T' +
            f(date.getUTCHours()) + ':' +
            f(date.getUTCMinutes()) + ':' +
            f(date.getUTCSeconds()) + 'Z' : null;
    };
})();
own.dateToJson = dateToJson;

//ie7下没有window.JSON，因此出现分支
export const encodeJson = (window.JSON && window.JSON.stringify) ? function (obj) {
    return window.JSON.stringify(obj);
} : (function () {

    var special = { '\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"': '\\"', '\\': '\\\\' };

    var escape = function (chr) {
        return special[chr] || '\\u' + ('0000' + chr.charCodeAt(0).toString(16)).slice(-4);
    };

    return function (obj) {

        switch (own.typeOf(obj)) {
            case 'string':
                return '"' + obj.replace(/[\x00-\x1f\\"]/g, escape) + '"';
            case 'array':
                return '[' + own.clean(own.map(obj, own.encodeJson)) + ']';
            case 'date':
                return own.encodeJson(own.dateToJson(obj));
            case 'object':
                var string = [];
                own.forEach(obj, function (value, key) {
                    var json = own.encodeJson(value);
                    if (json) {
                        string.push(own.encodeJson(key) + ':' + json);
                    }
                });
                return '{' + string + '}';
            case 'number':
            case 'boolean':
                return '' + obj;
            case 'null':
                return 'null';
        }

        return null;
    };
})();
own.encodeJson = encodeJson;

//ie7下没有window.JSON，因此出现分支
export const decodeJson = (window.JSON && window.JSON.parse) ? (string) => {
    if (!string || !own.isStr(string)) {
        return null;
    }
    return window.JSON.parse(string);
} : function (string) {
    if (!string || !own.isStr(string)) {
        return null;
    }
    return eval('(' + string + ')');
};
own.decodeJson = decodeJson;

export const toQueryString = (object, base) => {

    if (typeof object === 'string') {
        return object;
    }

    var queryString = [];

    forEach(object, (value, key) => {
        if (base) {
            key = base + '[' + key + ']';
        }
        var result;
        switch (typeOf(value)) {
            case 'object':
                result = toQueryString(value, key); break;
            case 'array':
                var qs = {};
                forEach(value, function (val, i) {
                    qs[i] = val;
                });
                result = toQueryString(qs, key);
                break;
            default:
                result = key + '=' + encodeURIComponent(value);
        }
        if (value != null) {
            queryString.push(result);
        }
    });

    return queryString.join('&');
};
own.toQueryString = toQueryString;

// search的逻辑重写
// type QueryStringFunction = {
//     (search: string): mergeObj
//     (search: string, prop: string): string
//     (search: string, prop: string, ...props: string[]): mergeObj
//     (search: mergeObj): string
//     (search: string, prop: mergeObj): string
// }

export const search = (search, prop, ...res) => {
    if (typeof search === 'string') {
        const newObj = {};

        // search('?a=1&b=2')
        if (isEmpty(prop)) { 
            search.replace(new RegExp('[?&]([\\S]+)*?[^&]*', 'g'), (expression) => {
                var exp = expression.replace(/^\s+|\s+$/g, '').slice(1),
                    exps;
                if (exp) {
                    exps = exp.split('=');
                    if (exps) {
                        newObj[exps[0]] = decodeURIComponent(exps[1] || '');
                    }
                }

                return expression;
            });
            return newObj;
        } else {
            if (isStr(prop)) {
                // search('?a=1&b=2', 'a', 'b', 'c');
                if (res.length) {
                    var match = search.match(new RegExp('[?&]' + prop + '=([^&]*)'));
                    newObj[prop] = match ? decodeURIComponent(match[1] || '') : undefined;
                    forEach(res, (prop) => {
                        var match = location.search.match(new RegExp('[?&]' + prop + '=([^&]*)'));
                        newObj[prop] = match ? decodeURIComponent(match[1] || '') : undefined;
                    });
                    return newObj;
                } else { // search('?a=1&b=2', 'a');
                    var match = location.search.match(new RegExp('[?&]' + prop + '=([^&]*)'));
                    return match ? match[1] || '' : undefined;
                }
            } else { // search('?a=1&b=2', { a: 3, c: 4})
                search.replace(new RegExp('[?&]([\\S]+)*?[^&]*', 'g'), (expression) => {
                    var exp = expression.replace(/^\s+|\s+$/g, '').slice(1),
                        exps;
                    if (exp) {
                        exps = exp.split('=');
                        if (exps) {
                            newObj[exps[0]] = exps[1] || '';
                        }
                    }
    
                    return expression;
                });
                assign(newObj, prop);
                return toQueryString(newObj);
            }
        }
    } else { // search({ a: 1, b: 2})
        return toQueryString(search);
    }
};
own.search = search;

let eventObject = {};

// 之前的addEvent，跟旧版相比，去掉了fn是方法数组的逻辑，同时同一个方法可以多次注册
export const on = (type, fn) => {
    if (isStr(type)) {
        if (!fn) {
            return;
        }

        var types = eventObject[type] || [];

        types.push(fn);

        eventObject[type] = types;
    }
    else {
        forEach(type, (value, key) => {
            on(key, value);
        });
    }
};
own.on = on;

// 旧版的fireEvent，去掉返回值规则
export const fire = (type, ...args) => {
    var events = eventObject[type];

    if (!events) {
        return;
    }

    forEach(events, (fn) => {
        fn.apply(this, args);
    }, this);
};
own.fire = fire;

// 旧版的removeEvent，移除对象形式的移除操作
export const off = (type, fn) => {
    if (!type) {
        eventObject = {};
    }
    else if (isStr(type)) {
        if (!fn) {
            eventObject[type] = [];
        }
        else {
            var events = eventObject[type];
            if (events) {
                erase(events, fn);
            }
        }
    }
};
own.off = off;

export default own;